#ifndef _DEPOOLHEAP_H
#define _DEPOOLHEAP_H
/*-------------------------------------------------------------------------
 * drawElements Memory Pool Library
 * --------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Memory pool heap class.
 *//*--------------------------------------------------------------------*/

#include "deDefs.h"
#include "deMemPool.h"
#include "dePoolArray.h"

DE_BEGIN_EXTERN_C

void			dePoolHeap_selfTest			(void);

DE_END_EXTERN_C

/*--------------------------------------------------------------------*//*!
 * \brief Declare a template pool heap class.
 * \param TYPENAME	Type name of the declared heap.
 * \param VALUETYPE	Type of the value contained in the heap.
 * \param CMPFUNC	Comparison function of two elements returning (-1, 0, +1).
 *
 * This macro declares a pool heap with all the necessary functions for
 * operating with it. All allocated memory is taken from the memory pool
 * given to the constructor.
 *
 * The functions for operating the heap are:
 *
 * \code
 * Heap*    Heap_create            (deMemPool* pool);
 * int      Heap_getNumElements    (const Heap* heap);
 * deBool   Heap_reserve           (Heap* heap, int size);
 * void		Heap_reset             (Heap* heap);
 * deBool   Heap_push              (Heap* heap, Element elem);
 * Element  Heap_popMin            (Heap* heap);
 * \endcode
*//*--------------------------------------------------------------------*/
#define DE_DECLARE_POOL_HEAP(TYPENAME, VALUETYPE, CMPFUNC)		\
    \
DE_DECLARE_POOL_ARRAY(TYPENAME##Array, VALUETYPE);		\
\
typedef struct TYPENAME##_s    \
{    \
	TYPENAME##Array*	array;		\
} TYPENAME; /* NOLINT(TYPENAME) */  \
\
DE_INLINE TYPENAME*	TYPENAME##_create			(deMemPool* pool);													\
DE_INLINE int		TYPENAME##_getNumElements	(const TYPENAME* heap)							DE_UNUSED_FUNCTION;	\
DE_INLINE deBool	TYPENAME##_reserve			(DE_PTR_TYPE(TYPENAME) heap, int capacity)		DE_UNUSED_FUNCTION;	\
DE_INLINE void		TYPENAME##_reset			(DE_PTR_TYPE(TYPENAME) heap)					DE_UNUSED_FUNCTION;	\
DE_INLINE void		TYPENAME##_moveDown			(DE_PTR_TYPE(TYPENAME) heap, int ndx)			DE_UNUSED_FUNCTION;	\
DE_INLINE void		TYPENAME##_moveUp			(DE_PTR_TYPE(TYPENAME) heap, int ndx)			DE_UNUSED_FUNCTION;	\
DE_INLINE deBool	TYPENAME##_push				(DE_PTR_TYPE(TYPENAME) heap, VALUETYPE elem)	DE_UNUSED_FUNCTION;	\
DE_INLINE VALUETYPE	TYPENAME##_popMin			(DE_PTR_TYPE(TYPENAME) heap)					DE_UNUSED_FUNCTION;	\
\
DE_INLINE TYPENAME* TYPENAME##_create (deMemPool* pool)    \
{    \
	DE_PTR_TYPE(TYPENAME) heap = DE_POOL_NEW(pool, TYPENAME);	\
	if (!heap)				\
		return DE_NULL;		\
	heap->array = TYPENAME##Array_create(pool);	\
	if (!heap->array)		\
		return DE_NULL;		\
	return heap;			\
}    \
\
DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* heap)    \
{    \
	return TYPENAME##Array_getNumElements(heap->array);    \
}    \
\
DE_INLINE deBool TYPENAME##_reserve (DE_PTR_TYPE(TYPENAME) heap, int capacity)    \
{    \
	return TYPENAME##Array_reserve(heap->array, capacity);    \
}    \
\
DE_INLINE void TYPENAME##_reset (DE_PTR_TYPE(TYPENAME) heap)    \
{    \
	TYPENAME##Array_setSize(heap->array, 0);    \
}    \
\
DE_INLINE void TYPENAME##_moveDown (DE_PTR_TYPE(TYPENAME) heap, int ndx)    \
{   \
	TYPENAME##Array*	array		= heap->array;	\
	int					numElements	= TYPENAME##Array_getNumElements(array);	\
	for (;;)	\
	{	\
		int childNdx0	= 2*ndx + 1;								\
		if (childNdx0 < numElements)	\
		{	\
			int childNdx1	= deMin32(childNdx0 + 1, numElements - 1);	\
			int childCmpRes	= CMPFUNC(TYPENAME##Array_get(array, childNdx0), TYPENAME##Array_get(array, childNdx1)); \
			int minChildNdx	= (childCmpRes == 1) ? childNdx1 : childNdx0;	\
			int cmpRes		= CMPFUNC(TYPENAME##Array_get(array, ndx), TYPENAME##Array_get(array, minChildNdx)); \
			if (cmpRes == 1)	\
			{	\
				TYPENAME##Array_swap(array, ndx, minChildNdx);	\
				ndx = minChildNdx;	\
			}	\
			else	\
				break;	\
		}	\
		else	\
			break;	\
	}	\
}    \
\
DE_INLINE void TYPENAME##_moveUp (DE_PTR_TYPE(TYPENAME) heap, int ndx)    \
{    \
	TYPENAME##Array* array = heap->array;	\
	while (ndx > 0)	\
	{	\
		int parentNdx	= (ndx-1) >> 1;								\
		int cmpRes		= CMPFUNC(TYPENAME##Array_get(array, ndx), TYPENAME##Array_get(array, parentNdx)); \
		if (cmpRes == -1)	\
		{	\
			TYPENAME##Array_swap(array, ndx, parentNdx);	\
			ndx = parentNdx;	\
		}	\
		else	\
			break;	\
	}	\
}    \
\
DE_INLINE deBool TYPENAME##_push (DE_PTR_TYPE(TYPENAME) heap, VALUETYPE elem)    \
{    \
	TYPENAME##Array* array = heap->array;	\
	int numElements = TYPENAME##Array_getNumElements(array);	\
	if (!TYPENAME##Array_setSize(array, numElements + 1)) \
		return DE_FALSE; \
	TYPENAME##Array_set(array, numElements, elem); \
	TYPENAME##_moveUp(heap, numElements);	\
	return DE_TRUE;    \
}    \
\
DE_INLINE VALUETYPE TYPENAME##_popMin (DE_PTR_TYPE(TYPENAME) heap)    \
{    \
	TYPENAME##Array* array = heap->array;	\
	VALUETYPE	tmp			= TYPENAME##Array_get(array, 0);	\
	int			numElements	= TYPENAME##Array_getNumElements(array);	\
	TYPENAME##Array_set(array, 0, TYPENAME##Array_get(array, numElements-1));	\
	TYPENAME##Array_setSize(array, numElements-1);	\
	TYPENAME##_moveDown(heap, 0);	\
	return tmp;	\
}    \
\
struct TYPENAME##Dummy_s { int dummy; }

#endif /* _DEPOOLHEAP_H */
